{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Configure-Runtime-Chain-Components\n",
    "\n",
    "- Author: [HeeWung Song(Dan)](https://github.com/kofsitho87)\n",
    "- Peer Review: \n",
    "- Proofread : [Chaeyoon Kim](https://github.com/chaeyoonyunakim)\n",
    "- This is a part of [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/13-LangChain-Expression-Language/06-Configure-Runtime-Chain-Components.ipynb)[![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/13-LangChain-Expression-Language/06-Configure-Runtime-Chain-Components.ipynb)\n",
    "## Overview\n",
    "\n",
    "In this tutorial, we will explore how to dynamically configure various options when calling a chain.\n",
    "\n",
    "There are two ways to implement dynamic configuration:\n",
    "\n",
    "- First, the ```configurable_fields``` method allows you to configure specific fields of a Runnable object.\n",
    "    - Dynamically modify specific field values at runtime\n",
    "    - Example: Adjust individual parameters like ```temperature```, ```model_name``` of an LLM\n",
    "\n",
    "- Second, the ```configurable_alternatives``` method lets you specify alternatives for a particular Runnable object that can be set during runtime\n",
    "    - Replace entire components with alternatives at runtime\n",
    "    - Example: Switch between different LLM models or prompt templates\n",
    "\n",
    "**[Note]**\n",
    "The term **Configurable fields** refers to settings or parameters within a system that can be adjusted or modified by the user or administrator at runtime.\n",
    "\n",
    "- Applying configuration\n",
    "    - ```with_config``` method: A unified interface for applying all configuration settings\n",
    "    - Ability to apply single or multiple settings simultaneously\n",
    "    - Used consistently across special components like ```HubRunnable```\n",
    "\n",
    "In the following sections, we'll cover detailed usage of each method and practical applications. We'll explore real-world examples including prompt management through ```HubRunnable``` setting various prompt alternatives, switching between LLM models, and more.\n",
    "\n",
    "### Table of Contents\n",
    "\n",
    "- [Overview](#overview)\n",
    "- [Environment Setup](#environment-setup)\n",
    "- [Configurable Fields](#configurable-fields)\n",
    "- [Configurable Alternatives with HubRunnables](#configurable-alternatives-with-hubrunnables)\n",
    "- [Switching between Runnables](#switching-between-runnables)\n",
    "- [Setting Prompt Alternatives](#setting-prompt-alternatives)\n",
    "- [Configuring Prompts and LLMs](#configuring-prompts-and-llms)\n",
    "- [Saving Configurations](#saving-configurations)\n",
    "\n",
    "\n",
    "### References\n",
    "\n",
    "- [LangChain How to configure runtime chain internals](https://python.langchain.com/docs/how_to/configure/)\n",
    "- [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/concepts/lcel/)\n",
    "- [LangChain Chaining runnables](https://python.langchain.com/docs/how_to/sequence/)\n",
    "- [LangChain HubRunnable](https://python.langchain.com/api_reference/langchain/runnables/langchain.runnables.hub.HubRunnable.html)\n",
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Environment Setup\n",
    "\n",
    "Setting up your environment is the first step. See the [Environment Setup](https://wikidocs.net/257836) guide for more details.\n",
    "\n",
    "**[Note]**\n",
    "- The ```langchain-opentutorial``` is a package of easy-to-use environment setup guidance, useful functions and utilities for tutorials.\n",
    "- Check out the [```langchain-opentutorial```](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "ename": "",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31mRunning cells with 'Python 3.9.6' requires the ipykernel package.\n",
      "\u001b[1;31mRun the following command to install 'ipykernel' into the Python environment. \n",
      "\u001b[1;31mCommand: '/usr/bin/python3 -m pip install ipykernel -U --user --force-reinstall'"
     ]
    }
   ],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install langchain-opentutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install required packages\n",
    "from langchain_opentutorial import package\n",
    "\n",
    "package.install(\n",
    "    [\n",
    "        \"langsmith\",\n",
    "        \"langchain\",\n",
    "        \"langchain_core\",\n",
    "        \"langchain_community\",\n",
    "        \"langchain_openai\",\n",
    "    ],\n",
    "    verbose=False,\n",
    "    upgrade=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set environment variables\n",
    "from langchain_opentutorial import set_env\n",
    "\n",
    "set_env(\n",
    "    {\n",
    "        \"OPENAI_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_TRACING_V2\": \"true\",\n",
    "        \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n",
    "        \"LANGCHAIN_PROJECT\": \"Configure-Runtime-Chain-Components\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, you can set and load ```OPENAI_API_KEY``` from a ```.env``` file.\n",
    "\n",
    "**[Note]** This is only necessary if you haven't already set ```OPENAI_API_KEY``` in previous steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configurable Fields\n",
    "\n",
    "```Configurable fields``` provide a way to dynamically modify specific parameters of a Runnable object at runtime. This feature is essential when you need to fine-tune the behavior of your chains or models without changing their core implementation.\n",
    "\n",
    "- They allow you to specify which parameters can be modified during execution\n",
    "- Each configurable field can include a description that explains its purpose\n",
    "- You can configure multiple fields simultaneously\n",
    "- The original chain structure remains unchanged, even when you modify configurations for different runs.\n",
    "\n",
    "The ```configurable_fields``` method is used to specify which parameters should be treated as configurable, making your LangChain applications more flexible and adaptable to different use cases."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Dynamic Property Configuration\n",
    "\n",
    "Let's illustrate this with ```ChatOpenAI```. When using ChatOpenAI, we can set various properties.\n",
    "\n",
    "The ```model_name``` property is used to specify the version of GPT. For example, you can select different models by setting it to ```gpt-4o```, ```gpt-4o-mini```, or else.\n",
    "\n",
    "To dynamically specify the model instead of using a fixed ```model_name```, you can leverage the ```ConfigurableField``` and assign it to a dynamically configurable property value as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_core.runnables import ConfigurableField\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(temperature=0, model_name=\"gpt-4o\")\n",
    "\n",
    "model.invoke(\"Where is the capital of the United States?\").__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = ChatOpenAI(temperature=0).configurable_fields(\n",
    "    # model_name is an original field of ChatOpenAI\n",
    "    model_name=ConfigurableField(\n",
    "        # Set the unique identifier of the field\n",
    "        id=\"gpt_version\",  \n",
    "        # Set the name for model_name\n",
    "        name=\"Version of GPT\",  \n",
    "        # Set the description for model_name\n",
    "        description=\"Official model name of GPTs. ex) gpt-4o, gpt-4o-mini\",\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When calling ```model.invoke()```, you can dynamically specify parameters using the format ```config={\"configurable\": {\"key\": \"value\"}}```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.invoke(\n",
    "    \"Where is the capital of the United States?\",\n",
    "    # Set gpt_version to gpt-3.5-turbo\n",
    "    config={\"configurable\": {\"gpt_version\": \"gpt-3.5-turbo\"}},\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's try using the ```gpt-4o-mini``` model. Check the output to see the changed model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.invoke(\n",
    "    # Set gpt_version to gpt-4o-mini\n",
    "    \"Where is the capital of the United States?\",\n",
    "    config={\"configurable\": {\"gpt_version\": \"gpt-4o-mini\"}},\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, you can set ```configurable``` parameters using the ```with_config()``` method of the ```model``` object to achieve the same result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.with_config(configurable={\"gpt_version\": \"gpt-4o-mini\"}).invoke(\n",
    "    \"Where is the capital of the United States?\",\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or you can also use this function as part of a chain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a prompt template from the template\n",
    "prompt = PromptTemplate.from_template(\"Select a random number greater than {x}\")\n",
    "chain = (\n",
    "    prompt | model\n",
    ")  # Create a chain by connecting prompt and model. The prompt's output is passed as input to the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain and pass 0 as the input variable \"x\"\n",
    "chain.invoke({\"x\": 0}).__dict__  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain with configuration settings\n",
    "chain.with_config(configurable={\"gpt_version\": \"gpt-4o\"}).invoke({\"x\": 0}).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configurable Alternatives with HubRunnables\n",
    "\n",
    "Using ```HubRunnable``` simplifies dynamic prompt selection, allowing easy switching between prompts registered in the Hub"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuring LangChain Hub Settings\n",
    "\n",
    "```HubRunnable``` provide an option to configure which prompt template to pull from the LangChain Hub. This enables you to dynamically select different prompts based on the hub path specification."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.runnables.hub import HubRunnable\n",
    "\n",
    "prompt = HubRunnable(\"rlm/rag-prompt\").configurable_fields(\n",
    "    # ConfigurableField for setting owner repository commit\n",
    "    owner_repo_commit=ConfigurableField(\n",
    "        # Field ID\n",
    "        id=\"hub_commit\",\n",
    "        # Field name\n",
    "        name=\"Hub Commit\",\n",
    "        # Field description\n",
    "        description=\"The Hub commit to pull from\",\n",
    "    )\n",
    ")\n",
    "prompt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you call the ```prompt.invoke()``` method without specifying a ```with_config```, the Runnable will automatically pull and use the prompt that was initially registered in the set **\\\"rlm/rag-prompt\\\"** hub."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the prompt object's invoke method with \"question\" and \"context\" parameters\n",
    "prompt.invoke({\"question\": \"Hello\", \"context\": \"World\"}).messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt.with_config(\n",
    "    # Set hub_commit to teddynote/summary-stuff-documents\n",
    "    configurable={\"hub_commit\": \"teddynote/summary-stuff-documents\"}\n",
    ").invoke({\"context\": \"Hello\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Switching between Runnables\n",
    "\n",
    "**Configurable alternatives** provide a way to select between different Runnable objects that can be set at runtime.\n",
    "\n",
    "For example, the configurable language model of ```ChatAnthropic``` provides high degree of flexibility that can be applied to various tasks and contexts.\n",
    "\n",
    "To enable dynamic switching, we can define the model's parameters as ```ConfigurableField``` objects.\n",
    "\n",
    "- ```model```: Specifies the base language model to be used.\n",
    "\n",
    "- ```temperature```: Controls the randomness of the model's sampling (which values between 0 and 1). Lower values result in more deterministic and repetitive outputs, while higher values lead to more diverse and creative responses."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setting Alternatives for LLM Objects\n",
    "\n",
    "Let's explore how to implement configurable alternatives using a Large Language Model (LLM).\n",
    "\n",
    "[Note]\n",
    "\n",
    "- To use the ```ChatAnthropic``` model, you need to obtain an API key from the Anthropic console: https://console.anthropic.com/dashboard.\n",
    "- You can uncomment and directly set the API key (as shown below) or store it in your ```.env``` file.\n",
    "\n",
    "Set the ```ANTHROPIC_API_KEY``` environment variable in your code.\n",
    "\n",
    "```python\n",
    "import os\n",
    "os.environ[\"ANTHROPIC_API_KEY\"] = \"Enter your ANTHROPIC API KEY here.\"\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.runnables import ConfigurableField\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "llm = ChatAnthropic(\n",
    "    temperature=0, model=\"claude-3-5-sonnet-20241022\"\n",
    ").configurable_alternatives(\n",
    "    # Assign an ID to this field.\n",
    "    # This ID will be used to configure the field when constructing the final runnable object.\n",
    "    ConfigurableField(id=\"llm\"),\n",
    "    # Set the default key.\n",
    "    # When this key is specified, it will use the default LLM (ChatAnthropic) initialized above.\n",
    "    default_key=\"anthropic\",\n",
    "    # Add a new option named 'openai', which is equivalent to `ChatOpenAI(model=\"gpt-4o-mini\")`.\n",
    "    openai=ChatOpenAI(model=\"gpt-4o-mini\"),\n",
    "    # Add a new option named 'gpt4o', which is equivalent to `ChatOpenAI(model=\"gpt-4o\")`.\n",
    "    gpt4o=ChatOpenAI(model=\"gpt-4o\"),\n",
    "    # You can add more configuration options here.\n",
    ")\n",
    "prompt = PromptTemplate.from_template(\"Please briefly explain about {topic}.\")\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's how you can invoke a chain using the default ```ChatAnthropic``` model using ```chain.invoke()```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Invoke using Anthropic as the default.\n",
    "chain.invoke({\"topic\": \"NewJeans\"}).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may specify a different model to use the ```llm``` by using ```chain.with_config(configurable={\"llm\": \"model\"})```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Invoke by changing the chain's configuration.\n",
    "chain.with_config(configurable={\"llm\": \"openai\"}).invoke({\"topic\": \"NewJeans\"}).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, change the chain's configuration to use ```gpt4o``` as the language model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Invoke by changing the chain's configuration.\n",
    "chain.with_config(configurable={\"llm\": \"gpt4o\"}).invoke({\"topic\": \"NewJeans\"}).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this time, change the chain's configuration to use ```anthropic```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Invoke by changing the chain's configuration.\n",
    "chain.with_config(configurable={\"llm\": \"anthropic\"}).invoke(\n",
    "    {\"topic\": \"NewJeans\"}\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting Prompt Alternatives\n",
    "\n",
    "Prompts can be configured in a similar pattern to the configuration of LLM alternatives that we previously set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialize the language model and set the temperature to 0.\n",
    "llm = ChatOpenAI(temperature=0)\n",
    "\n",
    "prompt = PromptTemplate.from_template(\n",
    "    # Default prompt template\n",
    "    \"Where is the capital of {country}?\"\n",
    ").configurable_alternatives(\n",
    "    # Assign an ID to this field.\n",
    "    ConfigurableField(id=\"prompt\"),\n",
    "    # Set the default key.\n",
    "    default_key=\"capital\",\n",
    "    # Add a new option named 'area'.\n",
    "    area=PromptTemplate.from_template(\"What is the area of {country}?\"),\n",
    "    # Add a new option named 'population'.\n",
    "    population=PromptTemplate.from_template(\"What is the population of {country}?\"),\n",
    "    # Add a new option named 'eng'.\n",
    "    kor=PromptTemplate.from_template(\"Translate {input} to Korean.\"),\n",
    "    # You can add more configuration options here.\n",
    ")\n",
    "\n",
    "# Create a chain by connecting the prompt and language model.\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If no configuration changes are made, the default prompt will be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain without any configuration changes.\n",
    "chain.invoke({\"country\": \"South Korea\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To use a different prompt, use ```with_config```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain by changing the chain's configuration using with_config.\n",
    "chain.with_config(configurable={\"prompt\": \"area\"}).invoke({\"country\": \"South Korea\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain by changing the chain's configuration using with_config.\n",
    "chain.with_config(configurable={\"prompt\": \"population\"}).invoke({\"country\": \"South Korea\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's use the ```kor``` prompt to request a translation, for example, pass the input using the ```input``` variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain by changing the chain's configuration using with_config.\n",
    "chain.with_config(configurable={\"prompt\": \"kor\"}).invoke({\"input\": \"apple is delicious!\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuring Prompts and LLMs\n",
    "\n",
    "You can configure multiple aspects using prompts and LLMs simultaneously.\n",
    "\n",
    "Here's an example that demonstrates how to use both prompts and LLMs to accomplish this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatAnthropic(\n",
    "    temperature=0, model=\"claude-3-5-sonnet-20241022\"\n",
    ").configurable_alternatives(\n",
    "    # Assign an ID to this field.\n",
    "    # When configuring the end runnable, we can then use this id to configure this field.\n",
    "    ConfigurableField(id=\"llm\"),\n",
    "    # Set the default key.\n",
    "    # When this key is specified, it will use the default LLM (ChatAnthropic) initialized above.\n",
    "    default_key=\"anthropic\",\n",
    "    # Add a new option named 'openai', which is equivalent to `ChatOpenAI(model=\"gpt-4o-mini\")`.\n",
    "    openai=ChatOpenAI(model=\"gpt-4o-mini\"),\n",
    "    # Add a new option named 'gpt4o', which is equivalent to `ChatOpenAI(model=\"gpt-4o\")`.\n",
    "    gpt4o=ChatOpenAI(model=\"gpt-4o\"),\n",
    "    # You can add more configuration options here.\n",
    ")\n",
    "\n",
    "prompt = PromptTemplate.from_template(\n",
    "    # Default prompt template\n",
    "    \"Describe {company} in 20 words or less.\"\n",
    ").configurable_alternatives(\n",
    "    # Assign an ID to this field.\n",
    "    # When configuring the end runnable, we can then use this id to configure this field.\n",
    "    ConfigurableField(id=\"prompt\"),\n",
    "    # Set the default key.\n",
    "    default_key=\"description\",\n",
    "    # Add a new option named 'founder'.\n",
    "    founder=PromptTemplate.from_template(\"Who is the founder of {company}?\"),\n",
    "    # Add a new option named 'competitor'.\n",
    "    competitor=PromptTemplate.from_template(\"Who is the competitor of {company}?\"),\n",
    "    # You can add more configuration options here.\n",
    ")\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can configure both the prompt and LLM simultaneously using .with_config(). Here we're using the founder prompt template with the OpenAI model.\n",
    "chain.with_config(configurable={\"prompt\": \"founder\", \"llm\": \"openai\"}).invoke(\n",
    "    # Request processing for the company provided by the user.\n",
    "    {\"company\": \"Apple\"}\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# If you want to configure the chain to use the Anthropic model, you can do so as follows:\n",
    "chain.with_config(configurable={\"llm\": \"anthropic\"}).invoke(\n",
    "    {\"company\": \"Apple\"}\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# If you want to configure the chain to use the competitor prompt template, you can do so as follows:\n",
    "chain.with_config(configurable={\"prompt\": \"competitor\"}).invoke(\n",
    "    {\"company\": \"Apple\"}\n",
    ").__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# If you want to use the default configuration, you can invoke the chain directly:\n",
    "chain.invoke({\"company\": \"Apple\"}).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saving Configurations\n",
    "\n",
    "You can easily save configured chains as reusable objects. For example, after configuring a chain for a specific task, you can save it for later use in similar tasks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Save the configured chain to a new variable.\n",
    "gpt4o_competitor_chain = chain.with_config(\n",
    "    configurable={\"llm\": \"gpt4o\", \"prompt\": \"competitor\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the chain.\n",
    "gpt4o_competitor_chain.invoke({\"company\": \"Apple\"}).__dict__"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
